import threading
import time
import logging
import pyaudio
from api_client import  get_customer_data, set_sms_content, \
    get_redis_value
from config_manager import get_config_manager
from doubao_api import RealTimeRecognizer, set_device_context
from logging_config import get_device_logger



class SimpleMultiDeviceManager:
    """简单多设备管理器 - 使用线程"""
    
    def __init__(self):
        self.devices = []
        self.threads = {}
        self.running = False
        self.device_mapping = {}  # 存储输入设备到输出设备的映射
        self._generic_logger = logging.getLogger(__name__)
        # 存储每个设备的recognizer实例（通过device_phone索引）
        self.device_recognizers: dict[str, RealTimeRecognizer] = {}  # {device_phone: RealTimeRecognizer}
        self._recognizers_lock = threading.Lock()  # 保护recognizers字典的锁

    def _log(self, message: str, level: int = logging.INFO, device_phone: str = ""):
        """统一日志入口，可按设备输出到对应tab"""
        if device_phone:
            logger = get_device_logger(device_phone)
            logger.log(level, message)
        else:
            self._generic_logger.log(level, message)
        
    def scan_audio_devices(self, target_devices=None):
        """扫描指定的音频输入设备
        
        Args:
            target_devices: 可选，自定义的目标设备映射列表，
                例如 [{ 'input_name': '789_in', 'output_name': '789_out', 'phone': '15655841102' }]
        """
        audio = pyaudio.PyAudio()
        devices = []
        seen_devices = set()  # 用于去重

        
        try:
            device_count = audio.get_device_count()

            for i in range(device_count):
                try:
                    info = audio.get_device_info_by_index(i)
                    device_name = info.get('name', f'Device_{i}')
                    max_input = int(info.get('maxInputChannels', 0))

                    # 检查是否是我们要找的输入设备
                    for target in target_devices:
                        if target['input_name'] in device_name and max_input > 0:
                            key = device_name.split(' ')[0]
                            # 检查是否已存在相同名称的设备
                            if key in seen_devices:
                                continue
                            seen_devices.add(key)
                            # 测试设备是否可用
                            try:
                                test_stream = audio.open(
                                    format=pyaudio.paInt16,
                                    channels=1,
                                    rate=16000,
                                    input=True,
                                    input_device_index=i,
                                    frames_per_buffer=1024
                                )
                                test_stream.close()
                                
                                devices.append({
                                    'index': i,
                                    'name': device_name,
                                    'phone': target['phone'],
                                    'target_output': target['output_name']
                                })
                                self._log(
                                    f"✅ 发现目标输入设备 {i}: {device_name}",
                                    device_phone=target.get('phone', '')
                                )
                                break
                            except Exception as test_e:
                                pass

                except Exception as e:
                    pass

        finally:
            audio.terminate()
            
        return devices
    
    # def scan_output_devices(self):
    #     """扫描可用的音频输出设备"""
    #     audio = pyaudio.PyAudio()
    #     output_devices = []
    #
    #     try:
    #         device_count = audio.get_device_count()
    #         for i in range(device_count):
    #             try:
    #                 info = audio.get_device_info_by_index(i)
    #                 if int(info.get('maxOutputChannels', 0)) > 0:
    #                     device_name = info.get('name', f'Output_Device_{i}')
    #                     output_devices.append({
    #                         'index': i,
    #                         'name': device_name
    #                     })
    #             except Exception as e:
    #                 print(f"获取输出设备 {i} 信息失败: {e}")
    #                 pass
    #     finally:
    #         audio.terminate()
    #
    #     return output_devices
    
    def find_output_device_for_input(self, input_device):
        """为输入设备查找对应的输出设备"""
        audio = pyaudio.PyAudio()
        try:
            device_count = audio.get_device_count()
            target_output_name = input_device.get('target_output', '')
            
            for i in range(device_count):
                try:
                    info = audio.get_device_info_by_index(i)
                    device_name = info.get('name', '')
                    max_output = int(info.get('maxOutputChannels', 0))
                    
                    if max_output > 0 and target_output_name in device_name:
                        self._log(
                            f"✅ 找到输出设备 {i}: {device_name}",
                            device_phone=input_device.get('phone', '')
                        )
                        return i, device_name
                except Exception as e:
                    continue
                    
            self._log(f"❌ 未找到输出设备: {target_output_name}", level=logging.WARNING,
                      device_phone=input_device.get('phone', ''))
            return None
        finally:
            audio.terminate()
    
    def start_all_devices(self):
        """启动所有设备监测"""
        if self.running:
            self._log("设备管理器已在运行，跳过启动")
            return
        
        # 确保之前的线程已经完全停止（但避免递归调用stop_all_devices）
        if self.threads:
            self._log("警告: 检测到仍有线程记录，正在清理...", level=logging.WARNING)
            # 只清理线程字典，不调用stop_all_devices（避免递归）
            old_threads = list(self.threads.items())
            self.threads.clear()
            # 等待旧线程自然结束（如果还在运行）
            for device_index, thread in old_threads:
                if thread.is_alive():
                    self._log(f"等待旧线程 {device_index} 结束...")
                    thread.join(timeout=2)
            # 确保running状态为False
            self.running = False
            time.sleep(0.5)  # 短暂等待确保状态同步
            
        self.running = True
        self.threads.clear()  # 清空线程字典

        # 分批启动设备，避免同时启动太多设备造成冲突
        for i, device in enumerate(self.devices):
            # 再次检查running状态，防止在启动过程中被停止
            if not self.running:
                self._log("启动过程中检测到停止信号，中止启动", level=logging.WARNING)
                break
                
            thread = threading.Thread(
                target=self._device_worker,
                args=(device,),
                name=f"Device_{device['index']}",
                daemon=True
            )
            thread.start()
            self.threads[device['index']] = thread

            # 每个设备启动后稍作延迟，避免资源冲突
            if i < len(self.devices) - 1:  # 不是最后一个设备
                time.sleep(0.5)
    
    def stop_all_devices(self):
        """停止所有设备监测"""
        if not self.running:
            return
            
        self._log("正在停止所有设备...")
        self.running = False
        
        # 等待所有线程结束（增加等待时间，确保线程真正退出）
        import threading
        threads_to_wait = list(self.threads.items())
        for device_index, thread in threads_to_wait:
            if thread.is_alive():
                self._log(f"等待设备 {device_index} 线程退出...")
                thread.join(timeout=5)  # 增加到5秒
                if thread.is_alive():
                    self._log(f"警告: 设备 {device_index} 线程未在5秒内退出", level=logging.WARNING)
                else:
                    self._log(f"设备 {device_index} 已停止")
        
        self.threads.clear()
        # 清空recognizers字典
        with self._recognizers_lock:
            self.device_recognizers.clear()
        self._log("所有设备已停止")

    def disconnect_device_ws(self, device_phone: str) -> bool:
        """断开指定设备的WebSocket连接（仅断开AI回复）
        
        Args:
            device_phone: 设备手机号
            
        Returns:
            bool: 是否成功断开（如果设备不存在或未连接，返回False）
        """
        with self._recognizers_lock:
            recognizer = self.device_recognizers.get(device_phone)
        
        if recognizer is None:
            self._log(f"设备 {device_phone} 未找到recognizer实例", level=logging.WARNING, device_phone=device_phone)
            return False
        
        try:
            recognizer.disconnect_ws_only()
            self._log(f"设备 {device_phone} WebSocket已断开", device_phone=device_phone)
            return True
        except Exception as e:
            self._log(f"断开设备 {device_phone} WebSocket失败: {e}", level=logging.ERROR, device_phone=device_phone)
            return False


    def _device_worker(self, device):
        """单个设备工作线程"""
        # 设置设备上下文，用于日志路由
        device_phone = device.get('phone', '')
        set_device_context(device_phone)

        def log(message: str, level: int = logging.INFO):
            self._log(message, level=level, device_phone=device_phone)
        
        recognizer = None
        retry_count = 0
        max_retries = 3
        consecutive_failures = 0
        max_consecutive_failures = 5

        try:
            while self.running:
                try:
                    # 为每个设备创建独立的识别器实例
                    if recognizer is None:
                        # 自动查找对应的输出设备
                        output_device_index,output_name = self.find_output_device_for_input(device)
                        if output_device_index is None:
                            log(f"⚠️  设备 {device['index']} 未找到对应的输出设备，跳过", level=logging.WARNING)
                            time.sleep(5)
                            continue

                        recognizer = RealTimeRecognizer(
                            device_index=device['index'],
                            device_phone=device['phone'],
                            output_device_index=output_device_index
                        )
                        # 确保识别器也使用设备上下文
                        recognizer.set_device_context(device_phone)
                        # 保存recognizer到字典，供外部控制使用
                        with self._recognizers_lock:
                            self.device_recognizers[device_phone] = recognizer
                        log(f"设备 {device['index']} ({device['name']}) -> 输出设备 {output_name}")
                    # 检查是否需要停止
                    if not self.running:
                        break
                    
                    recognizer.wait_until_dialing_for_device(stop_flag=lambda: not self.running)
                    
                    # 检查是否需要停止
                    if not self.running:
                        break
                    data: dict = get_customer_data(device_phone=recognizer.device_phone)
                    log(f"客户名: {data.get('customer_name', '未知')}")
                    try:
                        import uuid
                        # 直接使用设备自己的手机号，不调用全局的get_called_phone
                        recognizer.called_phone = data.get("phone", "")
                        recognizer.session_id_for_round = f"{recognizer.device_phone}_{recognizer.called_phone}"
                        log(f"设备 {recognizer.device_phone} session_id: {recognizer.session_id_for_round}")
                    except Exception as e:
                        import uuid
                        recognizer.session_id_for_round = str(uuid.uuid4())
                        log(f"设备 {recognizer.device_phone} 使用默认session_id: {recognizer.session_id_for_round}",
                            level=logging.WARNING)
                        phone_in_redis = data.get('phone', '')
                        log(
                            f"当前设备：{recognizer.device_phone}, 被拨打手机号：{recognizer.called_phone}，redis手机号：{phone_in_redis}",
                            level=logging.WARNING
                        )



                    config = get_config_manager().get_config()
                    sms_content = config.get("sms_content","")
                    if data.get("gender", "") == "男":
                        customer_name = data.get("customer_name", "")[:1] + "先生"
                    elif data.get("gender", "") == "女":
                        customer_name = data.get("customer_name", "")[:1] + "女士"
                    elif data.get("customer_name") == "客户":
                        customer_name = "客户"
                    else:
                        customer_name = data.get("customer_name", "")[:1] + "总"
                    sms_kwargs = {
                        "customer_name": customer_name
                    }
                    if sms_content:
                        sms_content = sms_content.format(**sms_kwargs)
                        log(f"短信内容: {sms_content}")
                    else:
                        sms_content = "尊敬的客户您好，点击直接添加我企业微信即可测额： iqnn.com.cn/9THPF ，后续业务经理1对1专属服务。"
                    set_sms_content(recognizer.device_phone,content=sms_content)

                    # 等待该设备的开关开启（检查停止标志）
                    nobody_connected = False
                    while self.running:  # 检查停止标志
                        # 检查设备状态
                        try:
                            status = get_redis_value(recognizer.redis_key)
                            if status:
                                if isinstance(status, str) and recognizer.called_phone not in status:
                                    nobody_connected = True
                                    break
                                if "completed" in status:
                                    nobody_connected = True
                                    break
                                if "connected" in status:
                                    break
                        except Exception as e:
                            log(f"检查连接状态异常: {e}", level=logging.WARNING)
                        
                        # 检查是否需要停止
                        if not self.running:
                            break
                        time.sleep(1)
                    
                    # 如果已停止，退出循环
                    if not self.running:
                        break
                    
                    if nobody_connected:
                        log(
                            f"客户：{data.get('customer_name', '')}, 手机号 {recognizer.called_phone} 30秒无人接通，挂断，进入下一个",
                            level=logging.WARNING
                        )
                        recognizer = None
                        continue  # 进入下一轮循环


                    # 开始一轮识别（传入客户信息，每次重新建立连接）
                    recognizer.start_recognition(customer_info=data)  # 传入客户信息
                    
                    # 检查是否需要停止
                    if not self.running:
                        recognizer.stop()
                        break
                    
                    recognizer.start_microphone_for_device()
                    
                    # 检查是否需要停止
                    if not self.running:
                        recognizer.stop()
                        break
                    
                    recognizer.send_audio()

                    # 一轮结束，收尾
                    recognizer.stop()
                    # set_guaduan_phone(recognizer.device_phone,"no")
                    # chats = recognizer.get_all_replies()
                    # print(chats)
                    # 从字典中移除recognizer
                    with self._recognizers_lock:
                        self.device_recognizers.pop(device_phone, None)
                    recognizer = None  # 重置识别器
                    retry_count = 0  # 重置重试计数
                    consecutive_failures = 0  # 重置连续失败计数
                    log("开始下一个号码处理---------》")
                except Exception as e:
                    log(f"设备 {device['index']} 处理异常: {e}", level=logging.ERROR)
                    import traceback
                    traceback.print_exc()
                    retry_count += 1
                    consecutive_failures += 1

                    # 清理当前识别器
                    if recognizer:
                        try:
                            recognizer.close()
                        except:
                            pass
                        # 从字典中移除recognizer
                        with self._recognizers_lock:
                            self.device_recognizers.pop(device_phone, None)
                        recognizer = None

                    # 检查是否需要停止
                    if not self.running:
                        break
                    
                    # 如果连续失败次数过多，暂停该设备（但要检查停止标志）
                    if consecutive_failures >= max_consecutive_failures:
                        log(f"设备 {device['index']} 连续失败次数过多，暂停 30 秒", level=logging.WARNING)
                        for _ in range(30):
                            if not self.running:
                                break
                            time.sleep(1)
                        consecutive_failures = 0
                        retry_count = 0
                    elif retry_count >= max_retries:
                        log(f"设备 {device['index']} 重试次数过多，暂停 {retry_count * 5} 秒",
                            level=logging.WARNING)
                        for _ in range(retry_count * 5):
                            if not self.running:
                                break
                            time.sleep(1)
                        retry_count = 0
                    else:
                        # 短暂等待后重试，但检查停止标志
                        for _ in range(20):  # 2秒 = 20 * 0.1秒
                            if not self.running:
                                break
                            time.sleep(0.1)

        except KeyboardInterrupt:
            log(f"设备 {device['index']} 收到中断信号", level=logging.WARNING)
        except Exception as e:
            log(f"设备 {device['index']} 工作线程异常: {e}", level=logging.ERROR)
            import traceback
            traceback.print_exc()
        finally:
            # 确保recognizer被正确清理
            if recognizer:
                try:
                    log(f"设备 {device['index']} 正在清理recognizer资源...")
                    recognizer.stop()
                    recognizer.close()
                except Exception as e:
                    log(f"清理recognizer资源时出错: {e}", level=logging.ERROR)
                # 从字典中移除recognizer
                with self._recognizers_lock:
                    self.device_recognizers.pop(device_phone, None)
                recognizer = None
            log(f"设备 {device['index']} 工作线程已退出")





